package com.shanling.shanlingcontroller.services.csr; /**************************************************************************************************
 * Copyright 2018 Qualcomm Technologies International, Ltd.                                       *
 **************************************************************************************************/



import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
import android.content.ContentValues;
import android.os.Handler;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.util.Log;

import com.qualcomm.qti.libraries.gaia.GAIA;
import com.qualcomm.qti.libraries.gaia.packets.GaiaPacketBREDR;
import com.qualcomm.qti.libraries.vmupgrade.UpgradeError;
import com.qualcomm.qti.libraries.vmupgrade.UpgradeManager;
import com.qualcomm.qti.libraries.vmupgrade.codes.ResumePoints;
import com.shanling.shanlingcontroller.CustomApplication;
import com.shanling.shanlingcontroller.R;
import com.shanling.shanlingcontroller.bean.BluetoothState;
import com.shanling.shanlingcontroller.bean.Constants;
import com.shanling.shanlingcontroller.bean.EventCenter;
import com.shanling.shanlingcontroller.bean.MybluetoothDevice;
import com.shanling.shanlingcontroller.utils.PreferenceUtil;
import com.shanling.shanlingcontroller.utils.ToastUtils;

import org.greenrobot.eventbus.EventBus;
import org.litepal.LitePal;

import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;

/**
 * <p>This class provides the tools to connect, communicate and disconnect with a BR/EDR device over RFCOMM using the
 * GAIA protocol. This class manages a BR EDR connection by extending {@link BREDRProvider BREDRProvider}.</p>
 * <p>This class analyzes the incoming data from a device in order to detect GAIA packets which are then provided to
 * a registered listener using {@link Messages#GAIA_PACKET}.</p>
 * <p>This class is also in charge of the upgrade process by instancing the
 * {@link UpgradeGaiaManager UpgradeGaiaManager} when an upgrade is requested by the application. This provider will
 * then throw update messages about the upgrade to the register listener using {@link Messages#UPGRADE_MESSAGE}. If
 * there is an upgrade going on, any data corresponding to a potential GAIA packet is sent to the Upgrade GAIA
 * Manager and any registered listener is no longer informed about them.</p>
 */
/*package*/ public  class GAIABREDRProvider extends BREDRProvider {
    /**
     * <p>The tag to display for logs.</p>
     */
    private final String TAG = "GAIABREDRProvider";
    /**
     * <p>To show the debug logs.</p>
     */
    private boolean mShowDebugLogs = false;
    /**
     * <p>The listener which is interested in events going on on this provider: connection state, errors, messages
     * received, etc.</p>
     */

    /**
     * The handler to use in order to delay some instructions in order to not block a current process.
     */
    private final Handler mHandler = new Handler();
    /**
     * The analyser of data used to build GAIA packets from bytes received from the Provider.
     */
    private final DataAnalyser mAnalyser = new DataAnalyser();





    // ====== ENUMS =====================================================================================

    /**
     * <p>All types of messages this provider can throw to a registered listener.</p>
     */
    @IntDef(flag = true, value = {GAIABREDRProvider.Messages.CONNECTION_STATE_HAS_CHANGED,
            GAIABREDRProvider.Messages.GAIA_PACKET, GAIABREDRProvider.Messages.ERROR,
            GAIABREDRProvider.Messages.GAIA_READY, GAIABREDRProvider.Messages.UPGRADE_MESSAGE })
    @Retention(RetentionPolicy.SOURCE)
    @SuppressLint("ShiftFlags") // values are more readable this way
    /*package*/ @interface Messages {

        /**
         * <p>To inform that the connection state with the given device has changed.</p>
         * <p>This type of {@link android.os.Message Message} also contains:</p>
         * <ul>
         *     <li>The connection state of the device as: {@link State#CONNECTED CONNECTED},
         *     {@link State#CONNECTING CONNECTING},
         *     {@link State#DISCONNECTING DISCONNECTING} or
         *     {@link State#DISCONNECTED DISCONNECTED}. This information is contained in
         *     <code>{@link android.os.Message#obj obj}</code>.</li>
         *     </ul>
         */
        int CONNECTION_STATE_HAS_CHANGED = 0;

        /**
         * <p>To inform that this provider has received a potential GAIA packet over an ongoing Bluetooth
         * connection.</p>
         * <p>This type of {@link android.os.Message Message} also contains:</p>
         * <ul>
         *     <li>A <code>byte</code> array which contains the data corresponding to a potential GAIA packet. This
         *     information is contained in <code>{@link android.os.Message#obj msg.obj}</code>.</li>
         * </ul>
         */
        int GAIA_PACKET = 1;

        /**
         * <p>To inform about any unexpected error which occurs during an ongoing connection or the connection
         * process itself.</p>
         * <p>This type of {@link android.os.Message Message} also contains:</p>
         * <ul>
         *     <li>The type of error as one of the {@link Errors Errors} values. This information is contained in
         *     <code>{@link android.os.Message#arg1 msg.arg1}</code>.</li>
         * </ul>
         */
        int ERROR = 2;

        /**
         * <p>This message is used to let the application know it can now communicate with the device using the GAIA
         * protocol.</p>
         * <p>This message does not contain any other information.</p>
         */
        int GAIA_READY = 3;

        int UPGRADE_MESSAGE = 4;
    }


    // ====== CONSTRUCTOR ===============================================================================

    /**
     * <p>Constructor of this class to get a provider of BR/EDR connection.</p>
     *
     * @param manager
     *          The BluetoothManager this provider should use to get a BluetoothAdapter. If this is null the provider
     *          will use {@link BluetoothAdapter#getDefaultAdapter() getDefaultAdapter}, known as less efficient than
     *          {@link BluetoothManager#getAdapter() getAdapter}.
     */
    public GAIABREDRProvider(BluetoothManager manager) {
        super(manager);
    }










    // ====== OVERRIDE SUPERCLASS METHODS ===========================================================

    @Override // BREDRProvider
    void showDebugLogs(boolean show) {
        mShowDebugLogs = show;
        Log.i(TAG, "Debug logs are now " + (show ? "activated" : "deactivated") + ".");
        super.showDebugLogs(show);
    }

    @Override // BREDRProvider
    void onConnectionStateChanged(@State int state) {
        switch (state){
            case  State.DISCONNECTED:
                CustomApplication.getInstance().setConnected(false);
                EventBus.getDefault().post(new EventCenter<>(Constants.BLUETOOTH_STATE, new BluetoothState(Constants.DISCONNECTED)));
                break;
            case State.CONNECTED:
                MybluetoothDevice mybluetoothDevice=new MybluetoothDevice(getDevice(),3,getDevice().getName(),getDevice().getAddress());
                PreferenceUtil preferenceUtil = PreferenceUtil.getInstance(CustomApplication.getInstance());
                preferenceUtil.setBleName(getDevice().getName());
                List<MybluetoothDevice> mybluetoothDevices = LitePal.findAll(MybluetoothDevice.class);
                preferenceUtil.setBleType(3);
                preferenceUtil.setBleMac(getDevice().getAddress());
                if (!mybluetoothDevices.contains(mybluetoothDevice)) {
                    mybluetoothDevice.save();
                }
                CustomApplication.getInstance().setConnected(true);
                EventBus.getDefault().post(new EventCenter<>(Constants.BLUETOOTH_STATE, new BluetoothState(Constants.CONNECTED)));
                break;
        }
        Log.e("weiyu","状态变了"+state);
        if (state == State.DISCONNECTED || state == State.DISCONNECTING) {
            mAnalyser.reset();

        }
    }

    public boolean sendGAIAUpgradePacket(byte[] packet, boolean isTransferringData) {
        return sendData(packet);
    }

    @Override // BREDRProvider
    void onConnectionError(@Errors int error) {
        Log.e("weiyu","连接断了"+error);
       CustomApplication.getInstance().setConnected(false);
        EventBus.getDefault().post(new EventCenter<>(Constants.BLUETOOTH_STATE, new BluetoothState(Constants.DISCONNECTED)));
    }

    @Override
    void onCommunicationRunning() {

    }


    @Override // BREDRProvider
    void onDataFound(byte[] data) {
        mAnalyser.analyse(data);
    }


    // ====== PRIVATE METHODS =========================================================================

    /**
     * <p>This method is called when the {@link DataAnalyser DataAnalyser} has built a potential GAIA packet from
     * incoming data from the connected device.</p>
     * <p>This method will dispatch the packet to its listener if there is no active upgrade.</p>
     *
     * @param data
     *          The potential packet.
     */
    private void onGAIAPacketFound(byte[] data) {
        EventBus.getDefault().postSticky(new EventCenter<>(Constants.GAIA_PACKET, data));
    }



    // ====== PRIVATE INNER CLASS ========================================================================

    /**
     * <p>This class analyses incoming data in order to build a packet corresponding to the GAIA protocol.</p>
     */
    private class DataAnalyser {
        /**
         * <p>This array contains the data received from the device and which might correspond to a GAIA packet.</p>
         */
        final byte[] mmData = new byte[GaiaPacketBREDR.MAX_PACKET];
        /**
         * <p>While building the data of a GAIA packet, this contain the flags information of the packet.</p>
         */
        int mmFlags;
        /**
         * <p>To get how many bytes had been received so far.</p>
         */
        int mmReceivedLength = 0;
        /**
         * <p>The number of bytes which are expected to build a current GAIA packet.</p>
         */
        int mmExpectedLength = GaiaPacketBREDR.MAX_PACKET;

        /**
         * <p>To reset the data of the analyser: no current packet at the moment.</p>
         */
        private void reset() {
            mmReceivedLength = 0;
            mmExpectedLength = GaiaPacketBREDR.MAX_PACKET;
        }

        /**
         * <p>This method will build a GAIA packet as defined in {@link GaiaPacketBREDR GaiaPacketBREDR}.</p>
         * <p>This method uses the data provided at each call to build a GAIA packet following this process:
         * <ol>
         *     <li>Looks for the start of the packet known as "start of frame": <code>{@link GaiaPacketBREDR#SOF SOF} =
         *     0xFF</code>.</li>
         *     <li>Gets the expected length of the GAIA packet using the bytes which follow SOF: flags and length.</li>
         *     <li>For each byte of a packet, copies the byte in the data array until it reaches the
         *     expectedLength.</li>
         *     <li>Calls {@link #onDataFound(byte[]) onDataFound} when the number of accumulated data reaches the
         *     expected length.</li>
         * </ol></p>
         *
         * @param data
         *          The data to analyse in order to build GAIA packet(s).
         */
        private void analyse(byte[] data) {
            int length = data.length;

            // go through the received data
            //noinspection ForLoopReplaceableByForEach // it is more efficient to not use foreach
            for (int i = 0; i < length; ++i) {
                // has started to get data of a GAIA packet
                if ((this.mmReceivedLength > 0) && (this.mmReceivedLength < GaiaPacketBREDR.MAX_PACKET)) {
                    // gets the data
                    mmData[this.mmReceivedLength] = data[i];

                    // gets the flags to know if there is a checksum which has impact on the GAIA packet length
                    if (this.mmReceivedLength == GaiaPacketBREDR.OFFSET_FLAGS)  { // = 2
                        mmFlags = data[i];
                    }
                    // gets the expected length
                    else if (this.mmReceivedLength == GaiaPacketBREDR.OFFSET_LENGTH) { // = 3
                        mmExpectedLength = data[i] // payload length
                                + GaiaPacketBREDR.OFFSET_PAYLOAD // number of bytes before
                                + (((mmFlags & GaiaPacketBREDR.FLAG_CHECK_MASK) != 0) ? 1 : 0);
                    }

                    // number of received bytes can be incremented
                    ++this.mmReceivedLength;

                    // if GAIA packet is complete, it is dispatched
                    if (this.mmReceivedLength == mmExpectedLength) {
                        byte[] packet = new byte[mmReceivedLength];
                        System.arraycopy(mmData, 0, packet, 0, mmReceivedLength);
                        reset();
                        onGAIAPacketFound(packet);
                    }
                }
                // look for the start of frame
                else if (data[i] == GaiaPacketBREDR.SOF) {
                    this.mmReceivedLength = 1;
                }
                // number of received bytes is too big for a GAIA packet
                else if (mmReceivedLength >= GaiaPacketBREDR.MAX_PACKET) {
                    Log.w(TAG, "Packet is too long: received length is bigger than the maximum length of a GAIA " +
                            "packet. Resetting analyser.");
                    reset();
                }
            }
        }

    }
}
